TypeScriptμμ νμ μμ API νΈμΆμ λ§μ€ν°νμ¬ κ°λ ₯νκ³ μ μ§ κ΄λ¦¬ κ°λ₯νλ©° μ€λ₯ μλ μΉ μ ν리μΌμ΄μ μ λ§λμΈμ. λͺ¨λ² μ¬λ‘ λ° κ³ κΈ κΈ°μ μ μμ보μΈμ.
Type-Safe API Calls with TypeScript: A Comprehensive Guide
νλ μΉ κ°λ°μμ APIμ μνΈ μμ©νλ κ²μ κΈ°λ³Έμ μΈ μμ μ λλ€. TypeScriptλ κ°λ ₯ν νμ μμ€ν μ ν΅ν΄ νμ μμ API νΈμΆμ κ°λ₯νκ² ν¨μΌλ‘μ¨ μ ν리μΌμ΄μ μ μμ μ±κ³Ό μ μ§ κ΄λ¦¬μ±μ 보μ₯νλ λ° μ€μν μ΄μ μ μ 곡ν©λλ€. μ΄ κ°μ΄λμμλ TypeScriptμ κΈ°λ₯μ νμ©νμ¬ λͺ¨λ² μ¬λ‘, κ³ κΈ κΈ°μ λ° μ€μ μμ λ₯Ό λ€λ£¨λ©΄μ κ°λ ₯νκ³ μ€λ₯ μλ API μνΈ μμ©μ ꡬμΆνλ λ°©λ²μ μ΄ν΄λ΄ λλ€.
Why Type Safety Matters for API Calls
APIλ₯Ό μ¬μ©ν λ κΈ°λ³Έμ μΌλ‘ μΈλΆ μμ€μμ μ€λ λ°μ΄ν°λ₯Ό μ²λ¦¬νκ² λ©λλ€. μ΄ λ°μ΄ν°κ° μμν νμκ³Ό νμ μΌμΉνμ§ μμ μ μμΌλ―λ‘ λ°νμ μ€λ₯ λ° μκΈ°μΉ μμ λμμ΄ λ°μν μ μμ΅λλ€. νμ μμ μ±μ μμ νλ λ°μ΄ν°κ° 미리 μ μλ ꡬ쑰λ₯Ό μ€μνλμ§ νμΈνμ¬ κ°λ° νλ‘μΈμ€ μ΄κΈ°μ μ μ¬μ μΈ λ¬Έμ λ₯Ό κ°μ§ν¨μΌλ‘μ¨ μ€μν λ³΄νΈ κ³μΈ΅μ μ 곡ν©λλ€.
- Reduced Runtime Errors: μ»΄νμΌ μ νμ κ²μ¬λ₯Ό ν΅ν΄ νλ‘λμ νκ²½μ λλ¬νκΈ° μ μ νμ κ΄λ ¨ μ€λ₯λ₯Ό μλ³νκ³ μμ ν μ μμ΅λλ€.
- Improved Code Maintainability: λͺ νν νμ μ μλ μ½λλ₯Ό λ μ½κ² μ΄ν΄νκ³ μμ ν μ μλλ‘ νμ¬ λ¦¬ν©ν λ§ μ€μ λ²κ·Έκ° λ°μν μνμ μ€μ λλ€.
- Enhanced Code Readability: νμ μ£Όμμ μ€μν λ¬Έμλ₯Ό μ 곡νμ¬ κ°λ°μκ° μμλλ λ°μ΄ν° ꡬ쑰λ₯Ό λ μ½κ² μ΄ν΄ν μ μλλ‘ ν©λλ€.
- Better Developer Experience: νμ κ²μ¬ λ° μλ μμ±μ λν IDE μ§μμ κ°λ°μ κ²½νμ ν¬κ² ν₯μμν€κ³ μ€λ₯ κ°λ₯μ±μ μ€μ λλ€.
Setting Up Your TypeScript Project
API νΈμΆμ μμνκΈ° μ μ TypeScript νλ‘μ νΈκ° μ€μ λμ΄ μλμ§ νμΈνμμμ€. μ²μλΆν° μμνλ κ²½μ° λ€μμ μ¬μ©νμ¬ μ νλ‘μ νΈλ₯Ό μ΄κΈ°νν μ μμ΅λλ€.
npm init -y
npm install typescript --save-dev
tsc --init
μ΄λ κ² νλ©΄ κΈ°λ³Έ TypeScript μ»΄νμΌλ¬ μ΅μ μ΄ μλ `tsconfig.json` νμΌμ΄ μμ±λ©λλ€. μ΄λ¬ν μ΅μ μ νλ‘μ νΈμ μꡬ μ¬νμ λ§κ² μ¬μ©μ μ§μ ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ λ μ격ν νμ κ²μ¬λ₯Ό μν΄ strict λͺ¨λλ₯Ό νμ±νν μ μμ΅λλ€.
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
Defining Types for API Responses
νμ μμ API νΈμΆμ λ¬μ±νλ 첫 λ²μ§Έ λ¨κ³λ APIμμ μμ ν κ²μΌλ‘ μμλλ λ°μ΄ν° ꡬ쑰λ₯Ό λνλ΄λ TypeScript νμ μ μ μνλ κ²μ λλ€. μ΄λ μΌλ°μ μΌλ‘ `interface` λλ `type` μ μΈμ μ¬μ©νμ¬ μνλ©λλ€.
Using Interfaces
μΈν°νμ΄μ€λ κ°μ²΄μ λͺ¨μμ μ μνλ κ°λ ₯ν λ°©λ²μ λλ€. μλ₯Ό λ€μ΄ APIμμ μ¬μ©μ λͺ©λ‘μ κ°μ Έμ€λ κ²½μ° λ€μκ³Ό κ°μ μΈν°νμ΄μ€λ₯Ό μ μν μ μμ΅λλ€.
interface User {
id: number;
name: string;
email: string;
address?: string; // Optional property
phone?: string; // Optional property
website?: string; // Optional property
company?: {
name: string;
catchPhrase: string;
bs: string;
};
}
μμ± μ΄λ¦ λ€μ `?`λ μμ±μ΄ μ ν μ¬νμμ λνλ λλ€. μ΄λ νΉμ νλκ° λλ½λ μ μλ API μλ΅μ μ²λ¦¬νλ λ° μ μ©ν©λλ€.
Using Types
νμ μ μΈν°νμ΄μ€μ μ μ¬νμ§λ§ μ λμ¨ νμ λ° κ΅μ°¨ νμ μ μ μνλ κΈ°λ₯μ ν¬ν¨νμ¬ λ λ§μ μ μ°μ±μ μ 곡ν©λλ€. νμ μ μ¬μ©νμ¬ μμ λμΌν κ²°κ³Όλ₯Ό μ»μ μ μμ΅λλ€.
type User = {
id: number;
name: string;
email: string;
address?: string; // Optional property
phone?: string; // Optional property
website?: string; // Optional property
company?: {
name: string;
catchPhrase: string;
bs: string;
};
};
κ°λ¨ν κ°μ²΄ ꡬ쑰μ κ²½μ° μΈν°νμ΄μ€μ νμ μ μ’ μ’ μνΈ κ΅ν κ°λ₯ν©λλ€. κ·Έλ¬λ νμ μ λ 볡μ‘ν μλ리μ€λ₯Ό μ²λ¦¬ν λ λμ± κ°λ ₯ν΄μ§λλ€.
Making API Calls with Axios
Axiosλ JavaScript λ° TypeScriptμμ API μμ²μ 보λ΄κΈ° μν μΈκΈ° μλ HTTP ν΄λΌμ΄μΈνΈμ λλ€. κΉ¨λνκ³ μ§κ΄μ μΈ APIλ₯Ό μ 곡νλ―λ‘ λ€μν HTTP λ©μλ, μμ² ν€λ λ° μλ΅ λ°μ΄ν°λ₯Ό μ½κ² μ²λ¦¬ν μ μμ΅λλ€.
Installing Axios
npm install axios
Making a Typed API Call
Axiosλ₯Ό μ¬μ©νμ¬ νμ μμ API νΈμΆμ μννλ €λ©΄ `axios.get` λ©μλλ₯Ό μ¬μ©νκ³ μ λ€λ¦μ μ¬μ©νμ¬ μμλλ μλ΅ νμ μ μ§μ ν μ μμ΅λλ€.
import axios from 'axios';
async function fetchUsers(): Promise {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
return response.data;
} catch (error) {
console.error('Error fetching users:', error);
throw error;
}
}
fetchUsers().then(users => {
users.forEach(user => {
console.log(user.name);
});
});
μ΄ μμμ `axios.get
Handling Different HTTP Methods
Axiosλ `GET`, `POST`, `PUT`, `DELETE` λ° `PATCH`λ₯Ό ν¬ν¨ν λ€μν HTTP λ©μλλ₯Ό μ§μν©λλ€. ν΄λΉ λ©μλλ₯Ό μ¬μ©νμ¬ λ€μν νμ μ API μμ²μ μνν μ μμ΅λλ€. μλ₯Ό λ€μ΄ μ μ¬μ©μλ₯Ό μμ±νλ €λ©΄ `axios.post` λ©μλλ₯Ό μ¬μ©ν μ μμ΅λλ€.
async function createUser(user: Omit): Promise {
try {
const response = await axios.post('https://jsonplaceholder.typicode.com/users', user);
return response.data;
} catch (error) {
console.error('Error creating user:', error);
throw error;
}
}
const newUser = {
name: 'John Doe',
email: 'john.doe@example.com',
address: '123 Main St',
phone: '555-1234',
website: 'example.com',
company: {
name: 'Example Corp',
catchPhrase: 'Leading the way',
bs: 'Innovative solutions'
}
};
createUser(newUser).then(user => {
console.log('Created user:', user);
});
μ΄ μμμ `Omit
Using the Fetch API
Fetch APIλ HTTP μμ²μ 보λ΄κΈ° μν λ΄μ₯ JavaScript APIμ λλ€. Axiosλ³΄λ€ κΈ°λ³Έμ μ΄μ§λ§ TypeScriptμ ν¨κ» μ¬μ©νμ¬ νμ μμ API νΈμΆμ λ¬μ±ν μλ μμ΅λλ€. μꡬ μ¬νμ λ§λ κ²½μ° μ’ μμ±μ μΆκ°νμ§ μκΈ° μν΄ μ νΈν μ μμ΅λλ€.
Making a Typed API Call with Fetch
Fetchλ₯Ό μ¬μ©νμ¬ νμ μμ API νΈμΆμ μννλ €λ©΄ `fetch` ν¨μλ₯Ό μ¬μ©ν λ€μ μμλλ μλ΅ νμ μ μ§μ νμ¬ μλ΅μ JSONμΌλ‘ νμ±ν μ μμ΅λλ€.
async function fetchUsers(): Promise {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: User[] = await response.json();
return data;
} catch (error) {
console.error('Error fetching users:', error);
throw error;
}
}
fetchUsers().then(users => {
users.forEach(user => {
console.log(user.name);
});
});
μ΄ μμμ `const data: User[] = await response.json();`λ TypeScriptμκ² μλ΅ λ°μ΄ν°λ₯Ό `User` κ°μ²΄μ λ°°μ΄λ‘ μ²λ¦¬ν΄μΌ νλ€κ³ μλ €μ€λλ€. μ΄λ₯Ό ν΅ν΄ TypeScriptλ νμ κ²μ¬ λ° μλ μμ±μ μνν μ μμ΅λλ€.
Handling Different HTTP Methods with Fetch
Fetchλ₯Ό μ¬μ©νμ¬ λ€μν νμ μ API μμ²μ μννλ €λ©΄ `method` λ° `body` μ΅μ κ³Ό κ°μ λ€μν μ΅μ κ³Ό ν¨κ» `fetch` ν¨μλ₯Ό μ¬μ©ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ μ μ¬μ©μλ₯Ό μμ±νλ €λ©΄ λ€μ μ½λλ₯Ό μ¬μ©ν μ μμ΅λλ€.
async function createUser(user: Omit): Promise {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: User = await response.json();
return data;
} catch (error) {
console.error('Error creating user:', error);
throw error;
}
}
const newUser = {
name: 'John Doe',
email: 'john.doe@example.com',
address: '123 Main St',
phone: '555-1234',
website: 'example.com',
company: {
name: 'Example Corp',
catchPhrase: 'Leading the way',
bs: 'Innovative solutions'
}
};
createUser(newUser).then(user => {
console.log('Created user:', user);
});
Handling API Errors
μ€λ₯ μ²λ¦¬λ API νΈμΆμ μ€μν μΈ‘λ©΄μ λλ€. APIλ λ€νΈμν¬ μ°κ²° λ¬Έμ , μλ² μ€λ₯ λ° μλͺ»λ μμ²μ ν¬ν¨νμ¬ μ¬λ¬ κ°μ§ μ΄μ λ‘ μ€ν¨ν μ μμ΅λλ€. μ ν리μΌμ΄μ μ΄ μΆ©λνκ±°λ μμμΉ λͺ»ν λμμ νμνμ§ μλλ‘ μ΄λ¬ν μ€λ₯λ₯Ό μ μμ μΌλ‘ μ²λ¦¬νλ κ²μ΄ μ€μν©λλ€.
Using Try-Catch Blocks
λΉλκΈ° μ½λμμ μ€λ₯λ₯Ό μ²λ¦¬νλ κ°μ₯ μΌλ°μ μΈ λ°©λ²μ try-catch λΈλ‘μ μ¬μ©νλ κ²μ λλ€. μ΄λ₯Ό ν΅ν΄ API νΈμΆ μ€μ λ°μνλ μμΈλ₯Ό catchνκ³ μ μ νκ² μ²λ¦¬ν μ μμ΅λλ€.
async function fetchUsers(): Promise {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
return response.data;
} catch (error) {
console.error('Error fetching users:', error);
// Handle the error, e.g., display an error message to the user
throw error; // Re-throw the error to allow calling code to handle it as well
}
}
Handling Specific Error Codes
APIλ μ’ μ’ λ°μν μ€λ₯ νμ μ λνλ΄κΈ° μν΄ νΉμ μ€λ₯ μ½λλ₯Ό λ°νν©λλ€. μ΄λ¬ν μ€λ₯ μ½λλ₯Ό μ¬μ©νμ¬ λ³΄λ€ κ΅¬μ²΄μ μΈ μ€λ₯ μ²λ¦¬λ₯Ό μ 곡ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ 404 Not Found μ€λ₯μ 500 Internal Server Errorμ λν΄ λ€λ₯Έ μ€λ₯ λ©μμ§λ₯Ό νμν μ μμ΅λλ€.
async function fetchUser(id: number): Promise {
try {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
return response.data;
} catch (error: any) {
if (error.response?.status === 404) {
console.log(`User with ID ${id} not found.`);
return null; // Or throw a custom error
} else {
console.error('Error fetching user:', error);
throw error;
}
}
}
fetchUser(123).then(user => {
if (user) {
console.log('User:', user);
} else {
console.log('User not found.');
}
});
Creating Custom Error Types
λ³΄λ€ λ³΅μ‘ν μ€λ₯ μ²λ¦¬ μλ리μ€μ κ²½μ° μ¬μ©μ μ§μ μ€λ₯ νμ μ μμ±νμ¬ λ€μν νμ μ API μ€λ₯λ₯Ό λνλΌ μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ λ³΄λ€ κ΅¬μ‘°νλ μ€λ₯ μ 보λ₯Ό μ 곡νκ³ μ€λ₯λ₯Ό λ³΄λ€ ν¨κ³Όμ μΌλ‘ μ²λ¦¬ν μ μμ΅λλ€.
class ApiError extends Error {
constructor(public statusCode: number, message: string) {
super(message);
this.name = 'ApiError';
}
}
async function fetchUser(id: number): Promise {
try {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
return response.data;
} catch (error: any) {
if (error.response?.status === 404) {
throw new ApiError(404, `User with ID ${id} not found.`);
} else {
console.error('Error fetching user:', error);
throw new ApiError(500, 'Internal Server Error'); //Or any other suitable status code
}
}
}
fetchUser(123).catch(error => {
if (error instanceof ApiError) {
console.error(`API Error: ${error.statusCode} - ${error.message}`);
} else {
console.error('An unexpected error occurred:', error);
}
});
Data Validation
TypeScriptμ νμ μμ€ν μ μ¬μ©νλλΌλ λ°νμμ APIμμ μμ νλ λ°μ΄ν°μ μ ν¨μ±μ κ²μ¬νλ κ²μ΄ μ€μν©λλ€. APIλ μκ³ μμ΄ μλ΅ κ΅¬μ‘°λ₯Ό λ³κ²½ν μ μμΌλ©° TypeScript νμ μ΄ APIμ μ€μ μλ΅κ³Ό νμ μλ²½νκ² λκΈ°νλμ§ μμ μ μμ΅λλ€.
Using Zod for Runtime Validation
Zodλ λ°νμ λ°μ΄ν° μ ν¨μ± κ²μ¬λ₯Ό μν λ리 μ¬μ©λλ TypeScript λΌμ΄λΈλ¬λ¦¬μ λλ€. λ°μ΄ν°μ μμ ꡬ쑰λ₯Ό μ€λͺ νλ μ€ν€λ§λ₯Ό μ μν λ€μ λ°νμμ ν΄λΉ μ€ν€λ§μ λν΄ λ°μ΄ν°μ μ ν¨μ±μ κ²μ¬ν μ μμ΅λλ€.
Installing Zod
npm install zod
Validating API Responses with Zod
Zodλ₯Ό μ¬μ©νμ¬ API μλ΅μ μ ν¨μ±μ κ²μ¬νλ €λ©΄ TypeScript νμ μ ν΄λΉνλ Zod μ€ν€λ§λ₯Ό μ μν λ€μ `parse` λ©μλλ₯Ό μ¬μ©νμ¬ λ°μ΄ν°μ μ ν¨μ±μ κ²μ¬ν μ μμ΅λλ€.
import { z } from 'zod';
const userSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
address: z.string().optional(),
phone: z.string().optional(),
website: z.string().optional(),
company: z.object({
name: z.string(),
catchPhrase: z.string(),
bs: z.string(),
}).optional(),
});
type User = z.infer;
async function fetchUsers(): Promise {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
const data = z.array(userSchema).parse(response.data);
return data;
} catch (error) {
console.error('Error fetching users:', error);
throw error;
}
}
μ΄ μμμ `z.array(userSchema).parse(response.data)`λ μλ΅ λ°μ΄ν°κ° `userSchema`λ₯Ό μ€μνλ κ°μ²΄μ λ°°μ΄μΈμ§ νμΈν©λλ€. λ°μ΄ν°κ° μ€ν€λ§λ₯Ό μ€μνμ§ μμΌλ©΄ Zodκ° μ€λ₯λ₯Ό λ°μμν€κ³ μ΄λ₯Ό μ μ νκ² μ²λ¦¬ν μ μμ΅λλ€.
Advanced Techniques
Using Generics for Reusable API Functions
μ λ€λ¦μ μ¬μ©νλ©΄ λ€μν νμ μ λ°μ΄ν°λ₯Ό μ²λ¦¬ν μ μλ μ¬μ¬μ© κ°λ₯ν API ν¨μλ₯Ό μμ±ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ λͺ¨λ API μλν¬μΈνΈμμ λ°μ΄ν°λ₯Ό κ°μ Έμ μ¬λ°λ₯Έ νμ μΌλ‘ λ°νν μ μλ μ λ€λ¦ `fetchData` ν¨μλ₯Ό λ§λ€ μ μμ΅λλ€.
async function fetchData(url: string): Promise {
try {
const response = await axios.get(url);
return response.data;
} catch (error) {
console.error(`Error fetching data from ${url}:`, error);
throw error;
}
}
// Usage
fetchData('https://jsonplaceholder.typicode.com/users').then(users => {
console.log('Users:', users);
});
fetchData<{ title: string; body: string }>('https://jsonplaceholder.typicode.com/todos/1').then(todo => {
console.log('Todo', todo)
});
Using Interceptors for Global Error Handling
Axiosλ μ½λμμ μ²λ¦¬νκΈ° μ μ μμ² λ° μλ΅μ κ°λ‘μ± μ μλ μΈν°μ ν°λ₯Ό μ 곡ν©λλ€. μΈν°μ ν°λ₯Ό μ¬μ©νμ¬ μ€λ₯ λ‘κΉ λλ μ¬μ©μμκ² μ€λ₯ λ©μμ§ νμμ κ°μ μ μ μ€λ₯ μ²λ¦¬λ₯Ό ꡬνν μ μμ΅λλ€.
axios.interceptors.response.use(
(response) => response,
(error) => {
console.error('Global error handler:', error);
// Display an error message to the user
return Promise.reject(error);
}
);
Using Environment Variables for API URLs
μ½λμ API URLμ νλ μ½λ©νμ§ μμΌλ €λ©΄ νκ²½ λ³μλ₯Ό μ¬μ©νμ¬ URLμ μ μ₯ν μ μμ΅λλ€. μ΄λ κ² νλ©΄ κ°λ°, μ€ν μ΄μ§ λ° νλ‘λμ κ³Ό κ°μ λ€μν νκ²½μ λ§κ² μ ν리μΌμ΄μ μ λ μ½κ² ꡬμ±ν μ μμ΅λλ€.
`.env` νμΌ λ° `dotenv` ν¨ν€μ§λ₯Ό μ¬μ©νλ μμ λλ€.
// .env
API_URL=https://api.example.com
// Install dotenv
npm install dotenv
// Import and configure dotenv
import * as dotenv from 'dotenv'
dotenv.config()
const apiUrl = process.env.API_URL || 'http://localhost:3000'; // provide a default value
async function fetchData(endpoint: string): Promise {
try {
const response = await axios.get(`${apiUrl}/${endpoint}`);
return response.data;
} catch (error) {
console.error(`Error fetching data from ${apiUrl}/${endpoint}:`, error);
throw error;
}
}
Conclusion
νμ μμ API νΈμΆμ κ°λ ₯νκ³ μ μ§ κ΄λ¦¬ κ°λ₯νλ©° μ€λ₯ μλ μΉ μ ν리μΌμ΄μ μ ꡬμΆνλ λ° νμμ μ λλ€. TypeScriptλ API μλ΅μ λν νμ μ μ μνκ³ , λ°νμμ λ°μ΄ν°μ μ ν¨μ±μ κ²μ¬νκ³ , μ€λ₯λ₯Ό μ μμ μΌλ‘ μ²λ¦¬ν μ μλ κ°λ ₯ν κΈ°λ₯μ μ 곡ν©λλ€. μ΄ κ°μ΄λμ μ€λͺ λ λͺ¨λ² μ¬λ‘ λ° κΈ°μ μ λ°λ₯΄λ©΄ API μνΈ μμ©μ νμ§κ³Ό μμ μ±μ ν¬κ² ν₯μμν¬ μ μμ΅λλ€.
TypeScriptμ Axios λ° Zodμ κ°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νλ©΄ API νΈμΆμ΄ νμ μμ νκ³ λ°μ΄ν° μ ν¨μ±μ΄ κ²μ¬λλ©° μ€λ₯κ° μ μμ μΌλ‘ μ²λ¦¬λλμ§ νμΈν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ λ³΄λ€ κ°λ ₯νκ³ μ μ§ κ΄λ¦¬ κ°λ₯ν μ ν리μΌμ΄μ μ λ§λ€ μ μμ΅λλ€.
TypeScriptμ νμ μμ€ν μ μ¬μ©νλλΌλ νμ λ°νμμ λ°μ΄ν°μ μ ν¨μ±μ κ²μ¬ν΄μΌ ν©λλ€. APIκ° λ³κ²½λ μ μμΌλ©° νμ μ΄ APIμ μ€μ μλ΅κ³Ό νμ μλ²½νκ² λκΈ°νλμ§ μμ μ μμ΅λλ€. λ°νμμ λ°μ΄ν°μ μ ν¨μ±μ κ²μ¬νλ©΄ μ ν리μΌμ΄μ μμ λ¬Έμ κ° λ°μνκΈ° μ μ μ μ¬μ μΈ λ¬Έμ λ₯Ό catchν μ μμ΅λλ€.
Happy coding!